using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using LeisureCenter.Api;
using LeisureCenter.Core;
using LeisureCenter.MenuItems;
using LeisureCenter.Tools;
using LeisureCenter.OptionsForms;
using LeisureCenter.LeisureCenterDataSetTableAdapters;

namespace LeisureCenter.BrowserItems
{
    public abstract class FileSystemBrowserItem : IBrowserItem
    {
        protected FileSystemInfo fileSystemInfo;
        protected IBrowserFolderItem folder;
        private string thumbnail;

        protected FileSystemBrowserItem(IBrowserFolderItem folder, FileSystemInfo fileSystemInfo)
        {
            this.fileSystemInfo = fileSystemInfo;
            this.folder = folder;
        }

        #region IBrowserItem Membres

        public virtual string Text
        {
            get { return fileSystemInfo.Name; }
            set { }
        }

        public abstract string IconName
        {
            get;
        }

        public virtual string Description
        {
            get
            {
                try
                {
                    return String.Format("{0}\nModified: {1}\nCreated: {2}", fileSystemInfo.Name, fileSystemInfo.LastWriteTime, fileSystemInfo.CreationTime);
                }
                catch (FileNotFoundException)
                {
                    return "Deleted or moved";
                }
                catch (IOException ex)
                {
                    return ex.Message;
                }
            }
        }

        public virtual IBrowserFolderItem Folder
        {
            get { return folder; }
        }

        public abstract void Execute();

        public virtual void AlternateExecute()
        {
            // nothing here
        }

        public virtual void OptionsExecute()
        {
            // nothing here
        }

        public virtual string Thumbnail
        {
            get
            {
                if (this.thumbnail == null)
                {
                    string path = this.fileSystemInfo.FullName;
                    while (Path.GetPathRoot(path) != path)
                    {
                        foreach (string ext in new string[] { ".jpg", ".png" })
                        {
                            string fileName = Path.ChangeExtension(path, ext);
                            if (File.Exists(fileName))
                            {
                                this.thumbnail = fileName;
                                return fileName;
                            }
                        }
                        path = Path.GetDirectoryName(path);
                    }
                    this.thumbnail = "";
                }
                return this.thumbnail;
            }
        }

        #endregion
    }

    public class FileSystemErrorBrowserItem : IBrowserItem
    {
        private string errorMessage;
        protected IBrowserFolderItem folder;

        public FileSystemErrorBrowserItem(IBrowserFolderItem folder, string errorMessage)
        {
            this.folder = folder;
            this.errorMessage = errorMessage;
        }

        #region IBrowserItem Membres

        public string Text { get { return this.errorMessage; } }

        public string IconName { get { return "error"; } }

        public void Execute()
        {
        }

        public string Description { get { return this.errorMessage; } }

        public IBrowserFolderItem Folder { get { return this.folder; } }

        public void AlternateExecute()
        {
        }

        public void OptionsExecute()
        {
        }

        public string Thumbnail { get { return ""; } }

        #endregion
    }

    interface IFileSystemBrowserObjectItem
    {
        string FileName { get; }
    }

    public class FileSystemBrowserObjectItem : FileSystemBrowserItem, IBrowserObjectItem, IFileSystemBrowserObjectItem
    {
        private string iconName = @"default";
        private MediaKind kind = MediaKind.Unknown;
        private string _text = string.Empty;

        public FileSystemBrowserObjectItem(IBrowserFolderItem folder, FileSystemInfo fileSystemInfo)
            : base(folder, fileSystemInfo)
        {
            Extension ext;
            if (Program.LeisureCenterInstance.Settings.Extensions.TryGetValue(fileSystemInfo.Extension, out ext))
            {
                this.kind = ext.Kind;
                switch (ext.Kind)
                {
                    case MediaKind.CD:
                        this.iconName = @"audiocd";
                        break;
                    case MediaKind.DVD:
                        this.iconName = @"dvdvideo";
                        break;
                    case MediaKind.Music:
                        this.iconName = @"Music";
                        break;
                    case MediaKind.Video:
                        this.iconName = @"video";
                        break;
                    case MediaKind.Picture:
                        this.iconName = @"picture";
                        break;
                }
            }
            _text = fileSystemInfo.Name;
        }

        public override string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public override string IconName
        {
            get { return this.iconName; }
        }

        public override string Description
        {
            get
            {
                try
                {
                    return String.Format("{0}\nSize: {3}\nModified: {1}\nCreated: {2}", fileSystemInfo.Name, fileSystemInfo.LastWriteTime, fileSystemInfo.CreationTime, Utils.FormatSize(((FileInfo)fileSystemInfo).Length));
                }
                catch (FileNotFoundException)
                {
                    return "Deleted or moved";
                }
                catch (IOException ex)
                {
                    return ex.Message;
                }
            }
        }

        public override void Execute()
        {
            if (this.kind == MediaKind.DVD)
            {
                Program.LeisureCenterInstance.PlaylistManager.OpenVideoDvd(FileName);
                return;
            }

            List<string> fileNames = new List<string>();
            int index = 0;
            bool selfSeen = false;
            foreach (IBrowserItem item in folder.Items)
            {
                if (item is IFileSystemBrowserObjectItem)
                {
                    if (selfSeen || item == this)
                    {
                        selfSeen = true;
                        fileNames.Insert(index, ((IFileSystemBrowserObjectItem)item).FileName);
                        index++;
                    }
                    else
                    {
                        fileNames.Add(((IFileSystemBrowserObjectItem)item).FileName);
                    }
                }
            }
            Program.LeisureCenterInstance.PlaylistManager.AddFromFileNames(fileNames, true);
            Program.LeisureCenterInstance.MainForm.FocusPlaylist();
        }

        public override void AlternateExecute()
        {
            Program.LeisureCenterInstance.PlaylistManager.AddFromFileName(this.FileName, false);
        }

        public override void OptionsExecute()
        {
            LibraryObjectOptionsDialog dialog = new LibraryObjectOptionsDialog(this.fileSystemInfo.FullName);
            using (dialog)
            {
                dialog.Title = this.fileSystemInfo.FullName;
                FilesTableAdapter filesTableAdapter = new FilesTableAdapter();
                LeisureCenterDataSet.FilesDataTable filesDataTable = filesTableAdapter.GetFilesByFilename(this.fileSystemInfo.FullName);
                if (filesDataTable != null && filesDataTable.Count > 0)
                {
                    LeisureCenterDataSet.FilesRow row = filesDataTable[0];
                    if (!row.IsRatingNull())
                        dialog.ratingBar1.Rating = (int)row.Rating;
                    else
                        dialog.ratingBar1.Rating = 0;
                    dialog.LibraryId = row.LibraryId;
                    if (dialog.Execute())
                    {
                        if (row.IsRatingNull() || row.Rating != dialog.ratingBar1.Rating || row.LibraryId != dialog.LibraryId)
                        {
                            if (dialog.ratingBar1.Rating > 0)
                                row.Rating = dialog.ratingBar1.Rating;
                            else
                                row.SetRatingNull();
                            row.LibraryId = dialog.LibraryId;
                            filesTableAdapter.Update(filesDataTable);
                        }
                    }
                }
            }
        }

        #region IFileSystemBrowserObjectItem Membres

        public string FileName
        {
            get { return fileSystemInfo.FullName; }
        }

        #endregion
    }

    public class FileSystemBrowserFolderItem : FileSystemBrowserItem, IBrowserFolderItem
    {
        private SortedDictionary<string, IBrowserItem> files;
        private SortedDictionary<string, IBrowserItem> folders;
        private BaseFileSystemMenuItem submenuItem;
        private FileSystemWatcher fileSystemWatcher;

        private void CreateItems()
        {
            string commonPart = null;
            List<FileSystemBrowserObjectItem> newFiles = new List<FileSystemBrowserObjectItem>();
            files = new SortedDictionary<string, IBrowserItem>(StringComparer.CurrentCultureIgnoreCase);
            folders = new SortedDictionary<string, IBrowserItem>(StringComparer.CurrentCultureIgnoreCase);
            DirectoryInfo dir = (DirectoryInfo)fileSystemInfo;
            try
            {
                foreach (FileSystemInfo info in dir.GetFileSystemInfos())
                {
                    if (info is DirectoryInfo)
                    {
                        if (info.Name != ".mp3" && info.Name != "_mp3")
                        {
                            folders.Add(info.Name, new FileSystemBrowserFolderItem(submenuItem, this, info, this.fileSystemWatcher));
                        }
                    }
                    else
                        if (info is FileInfo && Program.LeisureCenterInstance.Settings.Extensions.ContainsKey(info.Extension))
                        {
                            FileSystemBrowserObjectItem file = new FileSystemBrowserObjectItem(this, info);
                            files.Add(info.Name, file);
                            newFiles.Add(file);
                            if (commonPart == null)
                            {
                                commonPart = info.Name;
                            }
                            else
                            {
                                while (commonPart.Length > 0 && !info.Name.StartsWith(commonPart))
                                {
                                    commonPart = commonPart.Substring(0, commonPart.Length - 1);
                                }
                            }
                        }
                }

                if (!string.IsNullOrEmpty(commonPart) && commonPart.Length > 10 && newFiles.Count > 1)
                {
                    foreach (FileSystemBrowserObjectItem file in newFiles)
                    {
                        file.Text = "..." + file.Text.Substring(commonPart.Length);
                    }
                }
            }
            catch (IOException ex)
            {
                this.folders.Add("", new FileSystemErrorBrowserItem(this, ex.Message));
            }
        }

        public FileSystemBrowserFolderItem(BaseFileSystemMenuItem submenuItem, IBrowserFolderItem folder, FileSystemInfo fileSystemInfo, FileSystemWatcher fileSystemWatcher)
            : base(folder, fileSystemInfo)
        {
            this.submenuItem = submenuItem;
            try
            {
                if (fileSystemWatcher != null)
                    this.fileSystemWatcher = fileSystemWatcher;
                else
                {
                    this.fileSystemWatcher = new FileSystemWatcher(this.fileSystemInfo.FullName);
                    this.fileSystemWatcher.IncludeSubdirectories = true;
                    this.fileSystemWatcher.EnableRaisingEvents = true;
                }
                this.fileSystemWatcher.Created += new FileSystemEventHandler(fileSystemWatcher_CreatedOrDeleted);
                this.fileSystemWatcher.Deleted += new FileSystemEventHandler(fileSystemWatcher_CreatedOrDeleted);
                this.fileSystemWatcher.Renamed += new RenamedEventHandler(fileSystemWatcher_Renamed);

            }
            catch (ArgumentException ex)
            {
                this.folders = new SortedDictionary<string, IBrowserItem>();
                this.folders.Add("", new FileSystemErrorBrowserItem(this, ex.Message));
            }
        }

        ~FileSystemBrowserFolderItem()
        {
            this.fileSystemWatcher.Created -= new FileSystemEventHandler(fileSystemWatcher_CreatedOrDeleted);
            this.fileSystemWatcher.Deleted -= new FileSystemEventHandler(fileSystemWatcher_CreatedOrDeleted);
            this.fileSystemWatcher.Renamed -= new RenamedEventHandler(fileSystemWatcher_Renamed);
        }

        void fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
        {
            if (this.files != null && (Path.GetDirectoryName(e.FullPath).Equals(this.fileSystemInfo.FullName, StringComparison.InvariantCultureIgnoreCase)
                || Path.GetDirectoryName(e.OldFullPath).Equals(this.fileSystemInfo.FullName, StringComparison.InvariantCultureIgnoreCase)))
            {
                Refresh();
            }
        }

        void fileSystemWatcher_CreatedOrDeleted(object sender, FileSystemEventArgs e)
        {
            if (this.files != null && Path.GetDirectoryName(e.FullPath).Equals(this.fileSystemInfo.FullName, StringComparison.InvariantCultureIgnoreCase))
            {
                Refresh();
            }
        }

        protected virtual void OnItemsChanged()
        {
            EventHandler temp = this.ItemsChanged;
            if (temp != null)
            {
                if (!Program.LeisureCenterInstance.MainForm.InvokeRequired)
                    temp(this, null);
                else
                {
                    Program.LeisureCenterInstance.MainForm.Invoke(temp, this, null);
                }
            }
        }

        public override string IconName
        {
            get { return @"folder"; }
        }

        public override void Execute()
        {
            Program.LeisureCenterInstance.BrowserManager.Browse(this);
        }

        public override void OptionsExecute()
        {
            FileSystemFolderOptionsDialog dialog = new FileSystemFolderOptionsDialog(this.fileSystemInfo.FullName);
            using (dialog)
            {
                dialog.Execute();
            }
        }

        #region IBrowserFolderItem Membres

        public IEnumerable<IBrowserItem> Items
        {
            get
            {
                submenuItem.lastFolder = this;
                if (files == null)
                    CreateItems();
                IBrowserItem[] itemsArray = new IBrowserItem[folders.Count + files.Count];
                folders.Values.CopyTo(itemsArray, 0);
                files.Values.CopyTo(itemsArray, folders.Count);
                if (itemsArray.Length == 1 && itemsArray[0] is IBrowserFolderItem)
                    return ((IBrowserFolderItem)itemsArray[0]).Items;
                return itemsArray;
            }
        }

        public string Title { get { return this.fileSystemInfo.FullName; } }

        public void Refresh()
        {
            this.files = null;
            this.folders = null;
            OnItemsChanged();
        }

        public event EventHandler ItemsChanged;

        public override IBrowserFolderItem Folder
        {
            get
            {
                if (this.folder == null)
                {
                    DirectoryInfo dir = (DirectoryInfo)fileSystemInfo;
                    DirectoryInfo parent = dir.Parent;
                    if (parent != null)
                    {
                        this.folder = new FileSystemBrowserFolderItem(this.submenuItem, null, parent, this.fileSystemWatcher);
                    }
                }
                return this.folder;
            }
        }

        #endregion
    }
}
